package com.client.listeners;

import java.awt.Color;
import java.awt.Point;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.HashMap;
import java.util.HashSet;

import com.client.CommandSender;
import com.client.DrawArea;
import com.sendables.Circle;
import com.sendables.Cursor;
import com.sendables.DoodlePoint;
import com.sendables.Line;
import com.sendables.MultiPointLine;
import com.sendables.Sendable;
import com.sendables.Shape;
import com.sendables.Square;

public class DoodleListener implements MouseListener, MouseMotionListener, KeyListener, MouseWheelListener {
	private CommandSender					cs;
	private DrawArea						da;
	private int								clientId;
	private HashSet<Integer>				keys;
	private HashMap<Integer, DoodlePoint>	startPoints;
	private DoodlePoint						currentLocation;
	private MultiPointLine					mpl;

	private int								state	= 0;
	private final int						IDLE	= 0;
	private final int						DRAWING	= 1;

	public DoodleListener(CommandSender cs, DrawArea da)
	{
		startPoints = new HashMap<Integer, DoodlePoint>();
		keys = new HashSet<Integer>();
		clientId = -1;
		this.cs = cs;
		this.da = da;
	}

	public void setId(int id)
	{
		clientId = id;
	}

	private int[] rectOf(DoodlePoint startPoint, DoodlePoint currentPoint, boolean shiftDown)
	{
		int[] r = new int[4];
		r[2] = (int) Math.abs(startPoint.getY() - currentPoint.getY());
		r[3] = (int) Math.abs(startPoint.getX() - currentPoint.getX());
		if (!shiftDown)
		{
			r[0] = (int) Math.min(startPoint.getX(), currentPoint.getX());
			r[1] = (int) Math.min(startPoint.getY(), currentPoint.getY());
		} else
		{

			int temp = Math.min(r[2], r[3]);
			r[2] = temp;
			r[3] = temp;

			if (startPoint.getX() > currentPoint.getX())
			{
				r[0] = startPoint.getX() - temp;
			} else
			{
				r[0] = startPoint.getX();
			}

			if (startPoint.getY() > currentPoint.getY())
			{
				r[1] = startPoint.getY() - temp;
			} else
			{
				r[1] = startPoint.getY();
			}
		}
		return r;
	}

	private void post(int type, int[] rect, byte temp, byte filled, boolean erase)
	{
		Color c = erase ? Color.BLACK : Sendable.c;
		switch (type)
		{
			case KeyEvent.VK_C:
				cs
						.postShape(new Circle(clientId, c, new DoodlePoint(rect[0], rect[1]), rect[2], rect[3], filled,
								temp));
				break;
			case KeyEvent.VK_S:
				cs
						.postShape(new Square(clientId, c, new DoodlePoint(rect[0], rect[1]), rect[2], rect[3], filled,
								temp));
				break;
		}
	}

	@Override
	public void mouseDragged(MouseEvent arg0)
	{
		currentLocation = adjust(arg0.getPoint());
		if (state == DRAWING)
		{
			byte filled = ((arg0.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0) ? Shape.FILLED : Shape.HOLLOW;
			boolean isRightClick = (arg0.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0;
			System.out.println(isRightClick);
			boolean drawLine = true;
			if (startPoints.get(KeyEvent.VK_S) != null)
			{
				int[] rect = rectOf(startPoints.get(KeyEvent.VK_S), adjust(arg0.getPoint()),
						isKeyDown(KeyEvent.VK_SHIFT));
				post(KeyEvent.VK_S, rect, Shape.TEMPORARY, filled, isRightClick);
				drawLine = false;
			}
			if (startPoints.get(KeyEvent.VK_C) != null)
			{
				int[] rect = rectOf(startPoints.get(KeyEvent.VK_C), adjust(arg0.getPoint()),
						isKeyDown(KeyEvent.VK_SHIFT));
				post(KeyEvent.VK_C, rect, Shape.TEMPORARY, filled, isRightClick);
				drawLine = false;
			}
			if (drawLine)
			{
				cs.postShape(new Line(clientId, isRightClick ? Color.black : Sendable.c,
						startPoints.get(KeyEvent.VK_L), adjust(arg0.getPoint()), Shape.TEMPORARY));
				startPoints.put(KeyEvent.VK_L, adjust(arg0.getPoint()));
				mpl.addPoint(adjust(arg0.getPoint()));
			}
		}
		cs.postShape(new Cursor(clientId, Sendable.c, adjust(arg0.getPoint())));
	}

	private boolean isKeyDown(int keyType)
	{
		return keys.contains(keyType);
	}

	private DoodlePoint adjust(Point p)
	{
		return new DoodlePoint((int) p.getX(), (int) p.getY() - da.getTopPosition());
	}

	@Override
	public void mousePressed(MouseEvent e)
	{
		currentLocation = adjust(e.getPoint());
		state = DRAWING;
		if (isKeyDown(KeyEvent.VK_S))
		{
			startPoints.put(KeyEvent.VK_S, adjust(e.getPoint()));
		}
		if (isKeyDown(KeyEvent.VK_C))
		{
			startPoints.put(KeyEvent.VK_C, adjust(e.getPoint()));
		}
		if (!isKeyDown(KeyEvent.VK_C) && !isKeyDown(KeyEvent.VK_S))
		{
			startPoints.put(KeyEvent.VK_L, adjust(e.getPoint()));
			mpl = new MultiPointLine(clientId, Sendable.c, adjust(e.getPoint()));
		}
	}

	@Override
	public void mouseReleased(MouseEvent e)
	{
		currentLocation = adjust(e.getPoint());
		boolean drawLine = true;
		byte filled = ((e.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0) ? Shape.FILLED : Shape.HOLLOW;
		boolean isRightClick = e.getButton() == 3;
		System.out.println(isRightClick);
		if (startPoints.get(KeyEvent.VK_S) != null)
		{
			int[] rect = rectOf(startPoints.get(KeyEvent.VK_S), adjust(e.getPoint()), isKeyDown(KeyEvent.VK_SHIFT));
			post(KeyEvent.VK_S, rect, Shape.PERMANENT, filled, isRightClick);
			drawLine = false;
			startPoints.remove(KeyEvent.VK_S);
		}
		if (startPoints.get(KeyEvent.VK_C) != null)
		{
			int[] rect = rectOf(startPoints.get(KeyEvent.VK_C), adjust(e.getPoint()), isKeyDown(KeyEvent.VK_SHIFT));
			post(KeyEvent.VK_C, rect, Shape.PERMANENT, filled, isRightClick);
			drawLine = false;
			startPoints.remove(KeyEvent.VK_C);
		}
		if (drawLine)
		{
			cs.postShape(new Line(clientId, isRightClick ? Color.BLACK : Sendable.c, startPoints.get(KeyEvent.VK_L),
					adjust(e.getPoint()), Shape.TEMPORARY));
			startPoints.remove(KeyEvent.VK_L);
			mpl.addPoint(currentLocation);
			cs.postShape(mpl);
			mpl = null;
		}
		state = IDLE;
	}

	@Override
	public void mouseMoved(MouseEvent arg0)
	{
		currentLocation = adjust(arg0.getPoint());
		cs.postShape(new Cursor(clientId, Sendable.c, adjust(arg0.getPoint())));
	}

	@Override
	public void mouseClicked(MouseEvent e)
	{
	}

	@Override
	public void mouseEntered(MouseEvent e)
	{
	}

	@Override
	public void mouseExited(MouseEvent e)
	{
		cs.postShape(new Cursor(clientId, Sendable.c, new DoodlePoint(-1000, -1000)));
	}

	@Override
	public void keyPressed(KeyEvent e)
	{
		System.out.println("KEY PRESSED");
		keys.add(e.getKeyCode());
		if (startPoints.get(e.getKeyCode()) == null && e.getKeyCode() != KeyEvent.VK_SHIFT)
		{
			startPoints.put(e.getKeyCode(), currentLocation);
		}
	}

	@Override
	public void keyReleased(KeyEvent e)
	{
		byte filled = ((e.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0) ? Shape.FILLED : Shape.HOLLOW;
		boolean isRightClick = (e.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0;
		if (startPoints.get(e.getKeyCode()) != null && state == DRAWING)
		{
			post(e.getKeyCode(),
					rectOf(startPoints.get(e.getKeyCode()), currentLocation, isKeyDown(KeyEvent.VK_SHIFT)),
					Shape.PERMANENT, filled, isRightClick);
		}
		startPoints.remove(e.getKeyCode());
		if (state == DRAWING)
		{
			startPoints.put(KeyEvent.VK_L, currentLocation);
			mpl = new MultiPointLine(clientId, Sendable.c, currentLocation);
		}
		keys.remove(e.getKeyCode());
	}

	@Override
	public void keyTyped(KeyEvent e)
	{
		System.out.println("KEY TYPED");
	}

	@Override
	public void mouseWheelMoved(MouseWheelEvent arg0)
	{
		da.setTopPosition(arg0.getWheelRotation() * -20);
	}

}
